return TRUE;
}
+/*
+ * Create a copy of a file, supporting optional union/add behavior.
+ */
static gboolean
-checkout_file_from_input_at (OstreeRepo *self,
- OstreeRepoCheckoutAtOptions *options,
- GFileInfo *file_info,
- GVariant *xattrs,
- GInputStream *input,
- int destination_dfd,
- const char *destination_name,
- GCancellable *cancellable,
- GError **error)
+create_file_copy_from_input_at (OstreeRepo *repo,
+ OstreeRepoCheckoutAtOptions *options,
+ GFileInfo *file_info,
+ GVariant *xattrs,
+ GInputStream *input,
+ int destination_dfd,
+ const char *destination_name,
+ GCancellable *cancellable,
+ GError **error)
{
- int res;
+ g_autofree char *temp_filename = NULL;
+ const gboolean union_mode = options->overwrite_mode == OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES;
+ const gboolean add_mode = options->overwrite_mode == OSTREE_REPO_CHECKOUT_OVERWRITE_ADD_FILES;
if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_SYMBOLIC_LINK)
{
- do
- res = symlinkat (g_file_info_get_symlink_target (file_info),
- destination_dfd, destination_name);
- while (G_UNLIKELY (res == -1 && errno == EINTR));
- if (res == -1)
+ if (symlinkat (g_file_info_get_symlink_target (file_info),
+ destination_dfd, destination_name) < 0)
{
- if (errno == EEXIST && options->overwrite_mode == OSTREE_REPO_CHECKOUT_OVERWRITE_ADD_FILES)
+ /* Handle union/add behaviors if we get EEXIST */
+ if (errno == EEXIST && union_mode)
{
- /* Note early return */
- return TRUE;
+ /* Unioning? Let's unlink and try again */
+ (void) unlinkat (destination_dfd, destination_name, 0);
+ if (symlinkat (g_file_info_get_symlink_target (file_info),
+ destination_dfd, destination_name) < 0)
+ return glnx_throw_errno (error);
}
+ else if (errno == EEXIST && add_mode)
+ /* Note early return - we don't want to set the xattrs below */
+ return TRUE;
else
return glnx_throw_errno (error);
}
+ /* Process ownership and xattrs now that we made the link */
if (options->mode != OSTREE_REPO_CHECKOUT_MODE_USER)
{
- if (G_UNLIKELY (fchownat (destination_dfd, destination_name,
- g_file_info_get_attribute_uint32 (file_info, "unix::uid"),
- g_file_info_get_attribute_uint32 (file_info, "unix::gid"),
- AT_SYMLINK_NOFOLLOW) == -1))
- return glnx_throw_errno (error);
-
- if (xattrs)
- {
- if (!glnx_dfd_name_set_all_xattrs (destination_dfd, destination_name,
- xattrs, cancellable, error))
- return FALSE;
- }
- }
- }
- else if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_REGULAR)
- {
- g_autoptr(GOutputStream) temp_out = NULL;
- int fd;
- guint32 file_mode;
-
- file_mode = g_file_info_get_attribute_uint32 (file_info, "unix::mode");
- /* Don't make setuid files on checkout when we're doing --user */
- if (options->mode == OSTREE_REPO_CHECKOUT_MODE_USER)
- file_mode &= ~(S_ISUID|S_ISGID);
-
- do
- fd = openat (destination_dfd, destination_name, O_WRONLY | O_CREAT | O_EXCL, file_mode);
- while (G_UNLIKELY (fd == -1 && errno == EINTR));
- if (fd == -1)
- {
- if (errno == EEXIST && options->overwrite_mode == OSTREE_REPO_CHECKOUT_OVERWRITE_ADD_FILES)
- {
- /* Note early return */
- return TRUE;
- }
- else
- return glnx_throw_errno (error);
- }
- temp_out = g_unix_output_stream_new (fd, TRUE);
- fd = -1; /* Transfer ownership */
-
- if (!write_regular_file_content (self, options, temp_out, file_info, xattrs, input,
- cancellable, error))
- return FALSE;
- }
- else
- g_assert_not_reached ();
-
- return TRUE;
-}
-
-/*
- * This function creates a file under a temporary name, then rename()s
- * it into place. This implements union-like behavior.
- */
-static gboolean
-checkout_file_unioning_from_input_at (OstreeRepo *repo,
- OstreeRepoCheckoutAtOptions *options,
- GFileInfo *file_info,
- GVariant *xattrs,
- GInputStream *input,
- int destination_dfd,
- const char *destination_name,
- GCancellable *cancellable,
- GError **error)
-{
- g_autofree char *temp_filename = NULL;
-
- if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_SYMBOLIC_LINK)
- {
- if (!_ostree_make_temporary_symlink_at (destination_dfd,
- g_file_info_get_symlink_target (file_info),
- &temp_filename,
- cancellable, error))
- return FALSE;
-
- if (xattrs)
- {
- if (!glnx_dfd_name_set_all_xattrs (destination_dfd, temp_filename,
- xattrs, cancellable, error))
+ if (TEMP_FAILURE_RETRY (fchownat (destination_dfd, destination_name,
+ g_file_info_get_attribute_uint32 (file_info, "unix::uid"),
+ g_file_info_get_attribute_uint32 (file_info, "unix::gid"),
+ AT_SYMLINK_NOFOLLOW)) == -1)
+ return glnx_throw_errno_prefix (error, "fchownat");
+
+ if (xattrs != NULL &&
+ !glnx_dfd_name_set_all_xattrs (destination_dfd, destination_name,
+ xattrs, cancellable, error))
return FALSE;
}
- if (G_UNLIKELY (renameat (destination_dfd, temp_filename,
- destination_dfd, destination_name) == -1))
- return glnx_throw_errno (error);
}
else if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_REGULAR)
{
glnx_fd_close int temp_fd = -1;
g_autoptr(GOutputStream) temp_out = NULL;
guint32 file_mode;
+ GLnxLinkTmpfileReplaceMode replace_mode;
file_mode = g_file_info_get_attribute_uint32 (file_info, "unix::mode");
/* Don't make setuid files on checkout when we're doing --user */
cancellable, error))
return FALSE;
- if (!glnx_link_tmpfile_at (destination_dfd, GLNX_LINK_TMPFILE_REPLACE,
+ /* The add/union/none behaviors map directly to GLnxLinkTmpfileReplaceMode */
+ if (add_mode)
+ replace_mode = GLNX_LINK_TMPFILE_NOREPLACE_IGNORE_EXIST;
+ else if (union_mode)
+ replace_mode = GLNX_LINK_TMPFILE_REPLACE;
+ else
+ replace_mode = GLNX_LINK_TMPFILE_NOREPLACE;
+
+ if (!glnx_link_tmpfile_at (destination_dfd, replace_mode,
temp_fd, temp_filename, destination_dfd,
destination_name,
error))
cancellable, error))
return FALSE;
- if (options->overwrite_mode == OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_FILES)
- {
- if (!checkout_file_unioning_from_input_at (repo, options, source_info, xattrs, input,
- destination_dfd,
- destination_name,
- cancellable, error))
- return g_prefix_error (error, "Union checkout of %s to %s: ", checksum, destination_name), FALSE;
- }
- else
- {
- if (!checkout_file_from_input_at (repo, options, source_info, xattrs, input,
- destination_dfd,
- destination_name,
- cancellable, error))
- return g_prefix_error (error, "Checkout of %s to %s: ", checksum, destination_name), FALSE;
- }
+ if (!create_file_copy_from_input_at (repo, options, source_info, xattrs, input,
+ destination_dfd,
+ destination_name,
+ cancellable, error))
+ return g_prefix_error (error, "Copy checkout of %s to %s: ", checksum, destination_name), FALSE;
if (input)
{
--- /dev/null
+#!/bin/bash
+#
+# Copyright (C) 2011 Colin Walters <walters@verbum.org>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+set -euo pipefail
+
+. $(dirname $0)/libtest.sh
+
+id=$(id -u)
+
+if test ${id} != 0; then
+ skip "continued basic tests must be run as root (possibly in a container)"
+fi
+
+setup_test_repository "bare"
+
+echo "1..1"
+
+nextid=$(($id + 1))
+
+rm checkout-test2 -rf
+$OSTREE checkout test2 checkout-test2
+$OSTREE commit ${COMMIT_ARGS} -b test2 --tree=ref=test2 --owner-uid=$nextid
+$OSTREE ls test2 baz/cow > ls.txt
+assert_file_has_content ls.txt '-00644 '${nextid}' '${id}
+# As bare and running as root (e.g. Docker container), do some ownership tests
+# https://github.com/ostreedev/ostree/pull/801
+# Both hardlinks and copies should respect ownership, but we don't have -C yet;
+# add it when we do.
+for opt in -H; do
+ rm test2-co -rf
+ $OSTREE checkout ${opt} test2 test2-co
+ assert_streq "$(stat -c '%u' test2-co/baz/cow)" ${nextid}
+ assert_streq "$(stat -c '%u' test2-co/baz/alink)" ${nextid}
+done
+rm test2-co -rf
+# But user mode doesn't
+$OSTREE checkout -U test2 test2-co
+assert_streq "$(stat -c '%u' test2-co/baz/cow)" ${id}
+assert_streq "$(stat -c '%u' test2-co/baz/alink)" ${id}
+echo "ok ownership"